package at.donalfonso.ui.group;

import java.util.ArrayList;

import javax.jms.Message;
import javax.jms.MessageConsumer;
import javax.jms.MessageProducer;
import javax.jms.ObjectMessage;
import javax.jms.Queue;
import javax.jms.Session;

import at.donalfonso.fahrer.FahrerJMS;
import at.donalfonso.koch.KochJMS;
import at.donalfonso.loadbalancer.LoadBalancerJMS;
import at.donalfonso.model.Bestellung;
import at.donalfonso.model.LieferBestellung;
import at.donalfonso.model.Pizza.PizzaEnum;
import at.donalfonso.utility.JMSUtility;

public class BenchmarkControllerJMS {

	JMSUtility filiale1;
	JMSUtility filiale2;

	ArrayList<LieferBestellung> lbestellung1;
	ArrayList<LieferBestellung> lbestellung2;
	ArrayList<LieferBestellung> lbestellung3;

	private FahrerJMS fahrer1_1;
	private FahrerJMS fahrer1_2;
	private FahrerJMS fahrer2_1;
	private FahrerJMS fahrer2_2;

	private KochJMS koch1_1;
	private KochJMS koch1_2;
	private KochJMS koch2_1;
	private KochJMS koch2_2;
	
	private LoadBalancerJMS loadBalancer;
	
	String adresse = "street";

	private int benchmarkTime = 60;

	public static void main(String[] args) {

		BenchmarkControllerJMS benchmark = new BenchmarkControllerJMS();
		benchmark.runBenchmark();
	}

	/**
	 * Ablauf des JMS-Tests:
	 * Da nur Fahrer in die Adress-Queue schreiben können, reicht es für den Benchmark aus dass
	 * die Fahrer nach 60 Sekunden beendet werden und anschließend die Lieferbestellungen in der 
	 * Adress-Queue gezählt werden.
	 */

	public BenchmarkControllerJMS(){

		filiale1 = new JMSUtility(1);
		filiale2 = new JMSUtility(2);
		// Broker wird gestartet
		filiale1.runBroker();

		lbestellung1 =  new ArrayList<LieferBestellung>();;
		lbestellung2 =  new ArrayList<LieferBestellung>();;
		lbestellung3 =  new ArrayList<LieferBestellung>();;
	}


	public void prepare(){
		
		filiale1.purgeQueues();
		filiale2.purgeQueues();
		
		erzeugeNeueBestellungen();

		/* Die Fahrer werden als Threads gestartet und nach 60 Sekunden beendet. */
		fahrer1_1 = new FahrerJMS("Fahrer1", 1);
		fahrer1_2 = new FahrerJMS("Fahrer2", 1);
		fahrer2_1 = new FahrerJMS("Fahrer3", 2);
		fahrer2_2 = new FahrerJMS("Fahrer4", 2);
		
		koch1_1 = new KochJMS("jamie", 1);
		koch1_2 = new KochJMS("horst", 1);
		koch2_1 = new KochJMS("martina", 2);
		koch2_2 = new KochJMS("sarah", 2);

		fahrer1_1.setWaitTime(0);
		fahrer1_2.setWaitTime(0);
		fahrer2_1.setWaitTime(0);
		fahrer2_2.setWaitTime(0);
		
		koch1_1.waitTime = false;
		koch1_2.waitTime = false;
		koch2_1.waitTime = false;
		koch2_2.waitTime = false;
		
		loadBalancer = new LoadBalancerJMS("BenchmarkBalancer");

		/* Fahrer starten */
		fahrer1_1.start();
		fahrer1_2.start();
		fahrer2_1.start();
		fahrer2_2.start();
		
		/* Köche starten */
		koch1_1.start();
		koch1_2.start();
		koch2_1.start();
		koch2_2.start();

		/* Loadbalancer starten */
		loadBalancer.start();

		/* Vorbereiten der Lieferbestellungen */

		ArrayList<PizzaEnum> pizzawunsch;

		pizzawunsch = new ArrayList<PizzaEnum>();
		pizzawunsch.add(PizzaEnum.MARGARITA);
		pizzawunsch.add(PizzaEnum.MARGARITA);
		pizzawunsch.add(PizzaEnum.SALAMI);
		pizzawunsch.add(PizzaEnum.CARDINALE);

		/* 100 Lieferbestellungen 2x Margarita 1x Salami 1x Cardinale */
		for(int i=0;i<100;i++){
			LieferBestellung bestellung = new LieferBestellung(1, adresse, true, pizzawunsch);
			lbestellung1.add(bestellung);
		}

		pizzawunsch = new ArrayList<PizzaEnum>();
		pizzawunsch.add(PizzaEnum.MARGARITA);
		pizzawunsch.add(PizzaEnum.SALAMI);
		pizzawunsch.add(PizzaEnum.CARDINALE);

		/* 100 Lieferbestellungen 1x Margarita 1x Salami 1x Cardinale */
		for(int i=0;i<100;i++){
			LieferBestellung bestellung = new LieferBestellung(2, adresse, true, pizzawunsch);
			lbestellung2.add(bestellung);
		}

		pizzawunsch = new ArrayList<PizzaEnum>();
		pizzawunsch.add(PizzaEnum.MARGARITA);
		pizzawunsch.add(PizzaEnum.SALAMI);

		/* 100 Lieferbestellungen 2x Margarita 1x Salami 1x Cardinale */
		for(int i=0;i<100;i++){
			LieferBestellung bestellung = new LieferBestellung(1, adresse, true, pizzawunsch);
			lbestellung3.add(bestellung);
		}		
	}

	public void runBenchmark(){

		try{
			
			filiale1.getConnection().start();
			filiale2.getConnection().start();
			
			prepare();

			// Startsignal
			System.out.println("Um den Benchmark zu starten bitte <ENTER> drücken.");

			System.in.read();
			System.out.println("Benchmark gestartet ...");

			run();
			Thread.sleep(benchmarkTime*1000);

			stopBenchmark();
			System.out.println("Benchmark nach "+benchmarkTime+" Sekunden gestoppt ...");
			int count = countLieferBestellungen();
			System.out.println("\nEs wurden "+count+" Lieferbestellungen ausgelesen.");
			System.exit(0);
		}catch(Exception e){
			System.out.println(e);
		}
	}

	public void run(){
		try{


			Session session = filiale1.getConnection().createSession(true, Session.SESSION_TRANSACTED);
			Queue queue = filiale1.getQueueBestellungAufgenommen();
			MessageProducer producer;
			Message message;
			System.out.println("Schreibe Bestellungen in Queue: "+queue);

			for(LieferBestellung bestellung : lbestellung1){
				producer = session.createProducer(queue);
				message = session.createObjectMessage(bestellung);
				producer.send(message);
				producer.close();
				
				/* Für den Loadbalancer */
				message = session.createObjectMessage(bestellung);
				message.setStringProperty("STATUS", "NEU");
				producer = session.createProducer(filiale1.getTopicLieferung());
				producer.send(message);
				producer.close();
			}
			session.commit();
			session.close();

			session = filiale2.getConnection().createSession(true, Session.SESSION_TRANSACTED);
			queue = filiale2.getQueueBestellungAufgenommen();
			System.out.println("Schreibe Bestellungen in Queue: "+queue);

			for(LieferBestellung bestellung : lbestellung2){
				producer = session.createProducer(queue);
				message = session.createObjectMessage(bestellung);
				producer.send(message);
				producer.close();
				
				/* Für den Loadbalancer */
				message = session.createObjectMessage(bestellung);
				message.setStringProperty("STATUS", "NEU");
				producer = session.createProducer(filiale2.getTopicLieferung());
				producer.send(message);
				producer.close();
			}
			session.commit();
			session.close();

			session = filiale1.getConnection().createSession(true, Session.SESSION_TRANSACTED);
			queue = filiale1.getQueueBestellungAufgenommen();
			System.out.println("Schreibe Bestellungen in Queue: "+queue);

			for(LieferBestellung bestellung : lbestellung3){
				producer = session.createProducer(queue);
				message = session.createObjectMessage(bestellung);
				producer.send(message);
				producer.close();
				
				/* Für den Loadbalancer */
				message = session.createObjectMessage(bestellung);
				message.setStringProperty("STATUS", "NEU");
				producer = session.createProducer(filiale1.getTopicLieferung());
				producer.send(message);
				producer.close();
			}
			session.commit();
			session.close();

		}catch(Exception e){

		}
	}

	public void stopBenchmark(){
		fahrer1_1.stop();
		fahrer1_2.stop();
		fahrer2_1.stop();
		fahrer2_2.stop();
		
		koch1_1.stop();
		koch1_2.stop();
		koch2_1.stop();
		koch2_2.stop();
		
		loadBalancer.stop();
	}


	/* Liest alle Lieferbestellungen aus der angegebenen Adresse aus */
	public int countLieferBestellungen(){

		int count = 0;
		try{
			Session session = filiale1.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);

			Queue queue = session.createQueue(adresse);
			MessageConsumer consumer = session.createConsumer(queue);
			Message message;

			do{
				message = consumer.receive(1000);
				count++;
			}while (message != null);

		}catch(Exception e){
			System.out.println(e);
		}
		return count;
	}

	public void erzeugeNeueBestellungen(){
		try{
			Session session = filiale1.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
			MessageProducer prod = session.createProducer(filiale1.getQueueBestellungNeu());
			Bestellung b = null;
			ObjectMessage om;

			for (int i = 1; i < 250; i++) {
				b = new Bestellung(1);
				om = session.createObjectMessage(b);
				prod.send(om);
			}
			session.close();
			
			session = filiale2.getConnection().createSession(false, Session.AUTO_ACKNOWLEDGE);
			prod = session.createProducer(filiale2.getQueueBestellungNeu());
			
			for (int i = 1; i < 200; i++) {
				b = new Bestellung(2);
				om = session.createObjectMessage(b);
				prod.send(om);
			}			
			session.close();
			
		}catch(Exception e){
			System.out.println("Fehler beim erzeugen der Bestellungen");
		}
	}
}
